/*********************************************************************
* File: modApi.cpp
*
* \brief Mod API implementation. 
*
*Links functions, handles client commands, initializes DLL and misc stuff.
*
* \author Instituto Superior Tecnico de Lisboa, Portugal
* \author Technical University of Lisbon, Portugal
* 
* \author Agentes Autonomos e Sistemas Multiagente
* \author Autonomous Agents and Multiagent Systems
* 
* \version	1.0
* \date		2006/2007
*********************************************************************/
#include "modApi.h"
#include <time.h>
#include <string.h>
#include "engineApi.h"
#include "metaApi.h"
#include "namefunc.h"
#include "util/language.h"
#include "util/util.h"
#include "util/debug.h"
using namespace util;
#include "game/globals.h"
#include "game/gui.h"
#include "game/sounds.h"
#include "game/waypoint.h"
#include "game/Tournament.h"
#include "game/World.h"
using namespace game;
using namespace game::gui;
#include "agents/Agent.h"
using namespace agents;
#include "agents/globals.h"

#include <string>
using namespace std;

DLL_GLOBAL const Vector g_vecZero = Vector(0, 0, 0);

extern GETENTITYAPI other_GetEntityAPI;
extern GETNEWDLLFUNCTIONS other_GetNewDLLFunctions;
DLL_FUNCTIONS other_gFunctionTable;
SERVER_GETBLENDINGINTERFACE other_Server_GetBlendingInterface = NULL;

short g_sModelIndexLaser; // holds the index for the laser beam
short g_sModelIndexArrow; // holds the index for the arrow beam
float g_flBotCheckTime = 0.0;
int g_iCreateStartIndex = -1, g_iRemoveStartIndex = -1;
int storeAddAgentVars[4];
usermenu_t iUserInMenu = MENU_NONE;
extern bool g_isFakeClientCommand;

cvar_t g_cvarException;
cvar_t g_cvarAgentQuota;
cvar_t g_cvarAgentAutoVacate;
cvar_t g_cvarAgentUseRadio;
cvar_t g_cvarAgentBuy;

gamedll_funcs_t gGameDLLFunc;
enginefuncs_t   g_engfuncs;
globalvars_t   *gpGlobals;

HINSTANCE       h_Library = NULL;

GETENTITYAPI        other_GetEntityAPI = NULL;
GETNEWDLLFUNCTIONS  other_GetNewDLLFunctions = NULL;
GIVEFNPTRSTODLL     other_GiveFnptrsToDll = NULL;

// If we're using MS compiler, we need to specify the export parameter...
// Jozef Wagner - START
#if _MSC_VER > 1000
#pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1")
#pragma comment(linker, "/SECTION:.data,RW")
#endif
// Jozef Wagner - END

//=============================================================================
// Prototypes
//=============================================================================
void handleServerCommand();
unsigned short getBuildNumber();

void GameDLLInit( void );
void registerCVars();
bool isCounterStrikeV16();
void initializeTournament();

int DispatchSpawn( edict_t *pent );
void DispatchTouch( edict_t *pentTouched, edict_t *pentOther );
int DispatchSpawn_Post(edict_t *pent);
BOOL ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]);
void ClientDisconnect( edict_t *pEntity );
void ClientCommand( edict_t *pEntity );
void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax );
void ServerActivate_Post( edict_t *pEdictList, int edictCount, int clientMax );
void ServerDeactivate( void );

void StartFrame( void );

void handleSayMessage(const char* msg, edict_t* sender);
void handleSayTeamMessage(const char* msg, CounterStrikeTeams::Enumeration team, edict_t* sender);
void handleSayDeadMessage(const char* msg, edict_t* sender);
//=============================================================================
// Functions
//=============================================================================
/**
* 
**/
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	if (fdwReason == DLL_PROCESS_DETACH)
	{
		util::freeAllTheStuff();
		util::language::freeLanguage();

		if (h_Library)
			FreeLibrary(h_Library);

		// free our table of exported symbols
		if (!g_bIsMMPlugin)
			FreeNameFuncGlobals();
	}
	return true;
}

/**
* \brief Receive engine function table from engine.
*
* This appears to be the first DLL routine called by the engine, 
* so we do some setup operations here.
**/
void WINAPI GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
{
	// get the engine functions from the engine...
	memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t));
	gpGlobals = pGlobals;

	if (g_bIsMMPlugin)
		return;

	char szGameDLLName[256];
	util::getGameDirectory(szGameDLLName);

	// load the actual Counter-Strike Game DLL
#ifdef _WIN32
	strcat(szGameDLLName, "\\dlls");
	mkdir(szGameDLLName);
	strcat(szGameDLLName, "\\mp.dll");

	h_Library = LoadLibrary(szGameDLLName);

	if (!h_Library)
	{
		// try to extract the game DLL out of the Steam cache
		int size;
		unsigned char *filebuf = LOAD_FILE_FOR_ME("dlls/mp.dll", &size);

		if (filebuf)
		{
			FILE *fp = fopen(szGameDLLName, "wb");
			if (fp)
			{
				// dump the file and close it
				fwrite(filebuf, size, 1, fp);
				fclose(fp);
			}
			FREE_FILE(filebuf);
		}

		h_Library = LoadLibrary(szGameDLLName);
	}
#else
	strcat(szGameDLLName, "/dlls/cs_i386.so");
	h_Library = dlopen(szGameDLLName, RTLD_NOW);
#endif

	if (!h_Library)
		util::terminateOnError("Fail to load game DLL!");

	other_GetEntityAPI = (GETENTITYAPI)GetProcAddress(h_Library, "GetEntityAPI");
	other_GetNewDLLFunctions = (GETNEWDLLFUNCTIONS)GetProcAddress(h_Library, "GetNewDLLFunctions");
	other_GiveFnptrsToDll = (GIVEFNPTRSTODLL)GetProcAddress(h_Library, "GiveFnptrsToDll");

	GetEngineFunctions(pengfuncsFromEngine, NULL);

#ifdef _WIN32
	LoadSymbols(szGameDLLName);  // Load exported symbol table
	pengfuncsFromEngine->pfnFunctionFromName = FunctionFromName;
	pengfuncsFromEngine->pfnNameForFunction = NameForFunction;
#endif

	// give the engine functions to the other DLL...
	(*other_GiveFnptrsToDll)(pengfuncsFromEngine, pGlobals);
}



/**
* 
**/
C_DLLEXPORT int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion )
{
	memset( pFunctionTable, 0, sizeof( DLL_FUNCTIONS ) );

	if (!g_bIsMMPlugin)
	{
		// pass other DLLs engine callbacks to function table...
		if (!(*other_GetEntityAPI)(&other_gFunctionTable, INTERFACE_VERSION))
			return false;  // error initializing function table!!!

		gGameDLLFunc.dllapi_table = &other_gFunctionTable;
		gpGamedllFuncs = &gGameDLLFunc;

		memcpy( pFunctionTable, &other_gFunctionTable, sizeof( DLL_FUNCTIONS ) );
	}

	pFunctionTable->pfnGameInit = GameDLLInit;
	pFunctionTable->pfnSpawn = DispatchSpawn;
	pFunctionTable->pfnTouch = DispatchTouch;
	pFunctionTable->pfnClientConnect = ClientConnect;
	pFunctionTable->pfnClientDisconnect = ClientDisconnect;
	pFunctionTable->pfnClientCommand = ClientCommand;
	pFunctionTable->pfnServerActivate = ServerActivate;
	pFunctionTable->pfnServerDeactivate = ServerDeactivate;
	pFunctionTable->pfnStartFrame = StartFrame;

	return true;
}
/**
* 
**/
C_DLLEXPORT int GetEntityAPI_Post( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion )
{
	memset( pFunctionTable, 0, sizeof( DLL_FUNCTIONS ) );

	pFunctionTable->pfnSpawn = DispatchSpawn_Post;
	pFunctionTable->pfnServerActivate = ServerActivate_Post;

	return (true);
}


C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion)
{
	if (g_bIsMMPlugin)
		memset(pengfuncsFromEngine, 0, sizeof(enginefuncs_t));

	pengfuncsFromEngine->pfnChangeLevel = pfnChangeLevel;
	pengfuncsFromEngine->pfnFindEntityByString = pfnFindEntityByString;
	pengfuncsFromEngine->pfnEmitSound = pfnEmitSound;
	pengfuncsFromEngine->pfnClientCommand = pfnClientCommand;
	pengfuncsFromEngine->pfnMessageBegin = pfnMessageBegin;
	pengfuncsFromEngine->pfnMessageEnd = pfnMessageEnd;
	pengfuncsFromEngine->pfnWriteByte = pfnWriteByte;
	pengfuncsFromEngine->pfnWriteChar = pfnWriteChar;
	pengfuncsFromEngine->pfnWriteShort = pfnWriteShort;
	pengfuncsFromEngine->pfnWriteLong = pfnWriteLong;
	pengfuncsFromEngine->pfnWriteAngle = pfnWriteAngle;
	pengfuncsFromEngine->pfnWriteCoord = pfnWriteCoord;
	pengfuncsFromEngine->pfnWriteString = pfnWriteString;
	pengfuncsFromEngine->pfnWriteEntity = pfnWriteEntity;
	pengfuncsFromEngine->pfnRegUserMsg = pfnRegUserMsg;
	pengfuncsFromEngine->pfnClientPrintf = pfnClientPrintf;
	pengfuncsFromEngine->pfnCmd_Args = pfnCmd_Args;
	pengfuncsFromEngine->pfnCmd_Argv = pfnCmd_Argv;
	pengfuncsFromEngine->pfnCmd_Argc = pfnCmd_Argc;
	pengfuncsFromEngine->pfnSetClientMaxspeed = pfnSetClientMaxspeed;

	return true;
}


/**
* 
**/
C_DLLEXPORT int GetNewDLLFunctions( NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion )
{
	if (other_GetNewDLLFunctions == NULL)
		return false;

	// pass other DLLs engine callbacks to function table...
	if (!(*other_GetNewDLLFunctions)(pFunctionTable, interfaceVersion))
		return false;  // error initializing function table!!!

	gGameDLLFunc.newapi_table = pFunctionTable;
	return true;
}
/**
* 
**/
C_DLLEXPORT int Server_GetBlendingInterface(
	int version,
struct sv_blending_interface_s **ppinterface,
struct engine_studio_api_s *pstudio,
	float (*rotationmatrix)[3][4],
	float (*bonetransform)[MAXSTUDIOBONES][3][4]) 
{
	if (!other_Server_GetBlendingInterface)
		other_Server_GetBlendingInterface = (SERVER_GETBLENDINGINTERFACE)GetProcAddress(h_Library, "Server_GetBlendingInterface");

	if (!other_Server_GetBlendingInterface)
		return 0;

	return (*other_Server_GetBlendingInterface)(version, ppinterface, pstudio, rotationmatrix, bonetransform);
}
/**
* \brief Invoked once, after GiveFnptrsToDll and GetEntityAPI, to initialize the MOD.
**/
void GameDLLInit( void )
{
	util::debug::initialize();
	g_world = new World();

	registerCVars();
	char command[16];
	getServerCommandPrefix(command);
	REG_SVR_COMMAND(command, handleServerCommand); // Register server command

	g_world->isVersion16 = isCounterStrikeV16();

	// Initialize the agents array of structures
	memset(g_world->agents, 0, sizeof(g_world->agents));
	memset(g_agentCreationTabs, 0, sizeof(g_agentCreationTabs));

	initializeTournament();

	// Load the language resource file
	util::language::initialize();

	if (g_bIsMMPlugin)
		RETURN_META(MRES_IGNORED);
	(*other_gFunctionTable.pfnGameInit)();
}
/**
* \brief Registers client variables.
**/
void registerCVars()
{
	char varName[64];
	char varString[64];

	getServerCommandPrefix(varName);
	strcat(varName, "_exception");
	strcpy(varString, "0");
	g_cvarException.name = (char*) malloc((strlen(varName)+1)*sizeof(char*));
	g_cvarException.string = (char*) malloc((strlen(varString)+1)*sizeof(char*));;
	strcpy(g_cvarException.name, varName);
	strcpy(g_cvarException.string, varString);

	getServerCommandPrefix(varName);
	strcat(varName, "_quota");
	strcpy(varString, "0");
	g_cvarAgentQuota.name = (char*) malloc((strlen(varName)+1)*sizeof(char*));
	g_cvarAgentQuota.string = (char*) malloc((strlen(varString)+1)*sizeof(char*));;
	strcpy(g_cvarAgentQuota.name, varName);
	strcpy(g_cvarAgentQuota.string, varString);

	getServerCommandPrefix(varName);
	strcat(varName, "_autovacate");
	strcpy(varString, "0");
	g_cvarAgentAutoVacate.name = (char*) malloc((strlen(varName)+1)*sizeof(char*));
	g_cvarAgentAutoVacate.string = (char*) malloc((strlen(varString)+1)*sizeof(char*));;
	strcpy(g_cvarAgentAutoVacate.name, varName);
	strcpy(g_cvarAgentAutoVacate.string, varString);

	getServerCommandPrefix(varName);
	strcat(varName, "_agentuseradio");
	strcpy(varString, "1");
	g_cvarAgentUseRadio.name = (char*) malloc((strlen(varName)+1)*sizeof(char*));
	g_cvarAgentUseRadio.string = (char*) malloc((strlen(varString)+1)*sizeof(char*));;
	strcpy(g_cvarAgentUseRadio.name, varName);
	strcpy(g_cvarAgentUseRadio.string, varString);

	getServerCommandPrefix(varName);
	strcat(varName, "_agentbuy");
	strcpy(varString, "1");
	g_cvarAgentBuy.name = (char*) malloc((strlen(varName)+1)*sizeof(char*));
	g_cvarAgentBuy.string = (char*) malloc((strlen(varString)+1)*sizeof(char*));;
	strcpy(g_cvarAgentBuy.name, varName);
	strcpy(g_cvarAgentBuy.string, varString);

	getServerCommandPrefix(varName);
	strcat(varName, "_wptfolder");
	strcpy(varString, "wptdefault");
	g_cvarWPTDirname.name = (char*) malloc((strlen(varName)+1)*sizeof(char*));
	g_cvarWPTDirname.string = (char*) malloc((strlen(varString)+1)*sizeof(char*));;
	strcpy(g_cvarWPTDirname.name, varName);
	strcpy(g_cvarWPTDirname.string, varString);


	CVAR_REGISTER(&g_cvarException);
	CVAR_REGISTER(&g_cvarAgentQuota);
	CVAR_REGISTER(&g_cvarAgentAutoVacate);
	CVAR_REGISTER(&g_cvarWPTDirname);
	CVAR_REGISTER(&g_cvarAgentUseRadio);
	CVAR_REGISTER(&g_cvarAgentBuy);
}

/**
* \brief Checks whether the CounterStrike version is 1.6.
**/
bool isCounterStrikeV16()
{
	bool res = false;
	unsigned char *tempbuf;

	// only CS v1.6 has this file
	// use HL Engine function so that it would not be affected by Steam...
	int i;
	tempbuf = LOAD_FILE_FOR_ME("events/famas.sc", &i);
	if (tempbuf)
	{
		res = true;
		FREE_FILE(tempbuf);
	}

	return res;
}
/**
* \brief Initializes the tournament.
**/
void initializeTournament()
{
	char fileName[256];
	util::getGameDirectory(fileName);
	strcat(fileName, "/addons/tournament.cfg");
	g_tournament.load(fileName);

	if(g_tournament.isOn())
		g_tournament.initialize();
}
/**
* \brief Handles the "worldspawn" entity.
*
* One of the entities that will be found in every map is called "worldspawn". This entity 
* is always the first entity created and is "the world" in a map. This function handles the worldspawn 
* entity, and will precache all of the entities needed by the MOD as well as read the sv_cheats CVAR 
* and store the value of sv_cheats.
*
* \param[in] pent This is a pointer to the engine's edict structure which represents the agent.
**/
int DispatchSpawn( edict_t *pent )
{
	char *pClassname = (char *)STRING(pent->v.classname);

	if (strcmp(pClassname, "worldspawn") == 0)
	{
		// do level initialization stuff here...
		game::waypoints::reset();
		game::waypoints::load();

		PRECACHE_SOUND("weapons/xbow_hit1.wav");      // waypoint add
		PRECACHE_SOUND("weapons/mine_activate.wav");  // waypoint delete
		PRECACHE_SOUND("common/wpn_hudoff.wav");      // path add/delete start
		PRECACHE_SOUND("common/wpn_hudon.wav");       // path add/delete done
		PRECACHE_SOUND("common/wpn_moveselect.wav");  // path add/delete cancel
		PRECACHE_SOUND("common/wpn_denyselect.wav");  // path add/delete error

		g_sModelIndexLaser = PRECACHE_MODEL("sprites/laserbeam.spr");
		g_sModelIndexArrow = PRECACHE_MODEL( "sprites/arrow1.spr");

		g_world->setIsRoundOver(true);
		g_world->newRound();

		g_world->mapType = MapTypes::unknown;
	}

	else if (strcmp(STRING(pent->v.classname), "func_vip_safetyzone") == 0
		|| strcmp(STRING(pent->v.classname), "info_vip_safetyzone") == 0)
		g_world->mapType |= MapTypes::assassination; // assassination map

	else if (strcmp(STRING(pent->v.classname), "hostage_entity") == 0)
		g_world->mapType |= MapTypes::hostageRescue; // rescue map

	else if (strcmp(STRING(pent->v.classname), "func_bomb_target") == 0
		|| strcmp(STRING(pent->v.classname), "info_bomb_target") == 0)
		g_world->mapType |= MapTypes::bombDefuse; // defusion map

	if (g_bIsMMPlugin)
		RETURN_META_VALUE(MRES_IGNORED, 0);

	int result = (*other_gFunctionTable.pfnSpawn)(pent);

	// solves the agents unable to see through certain types of glass bug.
	// MAPPERS: NEVER EVER ALLOW A TRANSPARENT ENTITY TO WEAR THE FL_WORLDBRUSH FLAG !!!

	if (pent->v.rendermode == kRenderTransTexture)
		pent->v.flags &= ~FL_WORLDBRUSH; // clear the FL_WORLDBRUSH flag out of transparent ents

	return result;
}


/**
* \brief Solves the agents unable to see through certain types of glass bug.
* 
* MAPPERS: NEVER EVER ALLOW A TRANSPARENT ENTITY TO WEAR THE FL_WORLDBRUSH FLAG !!!
**/
int DispatchSpawn_Post(edict_t *pent)
{
	if (pent->v.rendermode == kRenderTransTexture)
		pent->v.flags &= ~FL_WORLDBRUSH; // clear the FL_WORLDBRUSH flag out of transparent ents

	RETURN_META_VALUE(MRES_IGNORED, 0);
}

/**
*
**/
void DispatchTouch( edict_t *pentTouched, edict_t *pentOther )
{
	Agent *agent = Agent::getInstance(pentOther);
	if (agent)
	{
		if (FClassnameIs(pentTouched, "info_bomb_target") ||
			FClassnameIs(pentTouched, "func_bomb_target"))
			agent->combat->setIsInBombSite(true);
	}
	if (g_bIsMMPlugin)
		RETURN_META(MRES_IGNORED);
	(*other_gFunctionTable.pfnTouch)(pentTouched, pentOther);
}


/**
* \brief Frame main loop. 
* 
* Invoked at each server frame. Loops through all agents and invokes its
* decision cycle.
**/
void StartFrame( void )
{
	static int i, index, player_index;
	static float msecdel = 0, msecnum = 0;

	g_agentQuota = (int)CVAR_GET_FLOAT(g_cvarAgentQuota.name);
	g_radioCommunicationActive = CVAR_GET_FLOAT(g_cvarAgentUseRadio.name) > 0;
	g_agentAutoVacate = CVAR_GET_FLOAT(g_cvarAgentAutoVacate.name) > 0;

	// Record some Stats of all Players on the Server
	for (player_index = 1; player_index <= gpGlobals->maxClients; player_index++)
	{
		edict_t *pPlayer = INDEXENT(player_index);
		int iStoreIndex = player_index - 1;
		if (!FNullEnt(pPlayer) && !pPlayer->free && (pPlayer->v.flags & (FL_CLIENT | FL_FAKECLIENT)))
		{
			g_threatTab[iStoreIndex].pEdict = pPlayer;
			g_threatTab[iStoreIndex].isUsed = true;
			g_threatTab[iStoreIndex].isAlive = util::isAlive(pPlayer);
			if (g_threatTab[iStoreIndex].isAlive)
			{
				g_threatTab[iStoreIndex].team = util::getTeam(pPlayer);
				g_threatTab[iStoreIndex].origin = pPlayer->v.origin;
				soundSimulateUpdate(iStoreIndex);
			}
		}
		else
		{
			g_threatTab[iStoreIndex].isUsed = false;
			g_threatTab[iStoreIndex].pEdict = NULL;
		}
	}

	g_frameDuration = g_world->getTime() - g_previousFrameTime;
	g_previousFrameTime = g_world->getTime();

	if (msecdel + msecnum / 1000 < g_world->getTime() - 0.5 ||
		msecdel > g_world->getTime())
	{
		msecdel = g_world->getTime() - 0.05;
		msecnum = 0;
	}

	g_msecVal = (g_world->getTime() - msecdel) * 1000 - msecnum; // optimal msec value since start of 1 sec period
	msecnum = (g_world->getTime() - msecdel) * 1000; // value we have to add to reach optimum

	// do we have to start a new 1 sec period?
	if (msecnum > 1000)
	{
		msecdel += msecnum / 1000;
		msecnum = 0;
	}

	if (g_msecVal < 1)
		g_msecVal = 1; // don't allow the msec delay to be too low
	else if (g_msecVal > 255)
		g_msecVal = 255; // don't allow it to last longer than 255 milliseconds either

	// Go through all active agents, calling their Think function
	for (int agentIndex = 0; agentIndex < gpGlobals->maxClients; agentIndex++)
	{
		if (g_world->agents[agentIndex])
		{
			// Use these try-catch blocks to prevent server crashes when error occurs
#ifndef _DEBUG
			try
			{
#endif
				g_world->agents[agentIndex]->main();
#ifndef _DEBUG
			}
			catch (...)
			{
				// Error occurred. Kick off all agents and print a warning message
				game::gui::removeAllAgents();
				SERVER_PRINT("*** POD-Agent internal error. For safety reasons, "
					"all agents have been removed. ***\n"
					"Please shutdown and restart your server.\n");
			}
#endif
		}
	}

	if (!IS_DEDICATED_SERVER() && !FNullEnt(g_world->hostEdict) &&
		!g_world->hostEdict->free)
	{
		if (g_editNoclip) // Noclip cheat on?
			g_world->hostEdict->v.movetype = MOVETYPE_NOCLIP;

		if (g_waypointOn)
			game::waypoints::editingThink();
	}

	// are we currently spawning agents and is it time to spawn one yet?
	if (g_agentCreationTime != 0.0 && g_agentCreationTime + 1.0 <= g_world->getTime())
	{
		index = 0;
		while(index < gpGlobals->maxClients)
		{
			while (!g_agentCreationTabs[index].bNeedsCreation && index < gpGlobals->maxClients)
				index++;

			if (index < gpGlobals->maxClients)
			{
				game::gui::createAgent(g_agentCreationTabs[index].pCallingEnt,
					g_agentCreationTabs[index].team,
					g_agentCreationTabs[index].name,  
					g_agentCreationTabs[index].mind, 
					g_agentCreationTabs[index].model);

				g_agentCreationTabs[index].bNeedsCreation = false;

				if (g_agentCreationTime != 0.0)
					g_agentCreationTime = g_world->getTime();

				g_flBotCheckTime = g_world->getTime() + 2.0;
			}
			else
				g_agentCreationTime = 0.0;
		}
		
		int count = 0;

		for (i = 0; i < gpGlobals->maxClients; i++)
		{
			if (g_world->agents[i])
				count++;
		}

		if (count > g_agentQuota && g_agentQuota >= 0)
		{
			g_agentQuota = count;
			CVAR_SET_FLOAT(g_cvarAgentQuota.name, (float)count);
		}
	}

	// check if time to see if an agent needs to be created...
	if (g_flBotCheckTime < g_world->getTime())
	{
		g_flBotCheckTime = g_world->getTime() + 2.0;

		if (g_agentQuota >= 0)
		{
			int count = 0, agentCount = 0;
			for (i = 0; i < gpGlobals->maxClients; i++)
			{
				if (g_threatTab[i].isUsed)
				{
					count++;
					if (g_world->agents[i])
						agentCount++;
				}
			}

			if (agentCount > g_agentQuota)
				game::gui::kickRandomAgent();

			if (g_agentAutoVacate)
			{
				if (agentCount < g_agentQuota && count < gpGlobals->maxClients - 1)
					game::gui::createAgent(NULL, NULL, NULL, NULL, NULL);

				if (count >= gpGlobals->maxClients)
					game::gui::kickRandomAgent();
			}
			else
			{
				if (agentCount < g_agentQuota && count < gpGlobals->maxClients)
					game::gui::createAgent(NULL, NULL, NULL, NULL, NULL);
			}
		}
	}

	if (g_bIsMMPlugin)
		RETURN_META(MRES_IGNORED);

	(*other_gFunctionTable.pfnStartFrame)();
}

/**
* \brief Computes the version number.
**/
unsigned short getBuildNumber()
{
	const char *date = __DATE__;

	char *mon[12] =
	{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
	unsigned char mond[12] = 
	{ 31,    28,    31,    30,    31,    30,    31,    31,    30,    31,    30,    31 };

	int m = 0, d = 0, y = 0;

	for (m = 0; m < 11; m++)
	{
		if (strncmp(&date[0], mon[m], 3) == 0)
			break;
		d += mond[m];
	}

	d += atoi( &date[4] ) - 1;
	y = atoi( &date[7] ) - 2000;

	unsigned short usBuildNumber = d + (int)((y - 1) * 365.25);

	// Is it a leap year?
	if ((y % 4) == 0 && m > 1)
		usBuildNumber += 1;

	return usBuildNumber - 1114;
}
/**
*
**/
BOOL ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128])
{
	// check if this client is the listen server client
	if (strcmp(pszAddress, "loopback") == 0)
		g_world->hostEdict = pEntity; // save the edict of the listen server client...

	if (g_bIsMMPlugin)
		RETURN_META_VALUE(MRES_IGNORED, 0);

	return (*other_gFunctionTable.pfnClientConnect)(pEntity, pszName, pszAddress, szRejectReason);
}

/**
*
**/
void ClientDisconnect( edict_t *pEntity )
{
	// Check if its an agent
	int i = ENTINDEX(pEntity) - 1;
	assert(i >= 0);
	assert(i < 32);

	if (g_world->agents[i])
	{
		if (g_world->agents[i]->pev == &pEntity->v)
		{
			delete g_world->agents[i];
			g_world->agents[i] = NULL;
		}
	}

	if (g_bIsMMPlugin)
		RETURN_META (MRES_IGNORED);
	(*other_gFunctionTable.pfnClientDisconnect)(pEntity);
}

/**
*
**/
void ClientCommand( edict_t *pEntity )
{
	char command[64];

	const char *pcmd = CMD_ARGV(0);
	const char *arg1 = CMD_ARGV(1);
	static int iFillServerTeam = 5;

	if (util::isAlive(pEntity))
	{
		// Check radio commands
		int iClientIndex = ENTINDEX(pEntity) - 1;

		if (FStrEq(pcmd, "radio1"))
			g_radioSelect[iClientIndex] = 1;
		else if (FStrEq(pcmd, "radio2"))
			g_radioSelect[iClientIndex] = 3;
		else if (FStrEq(pcmd, "radio3"))
			g_radioSelect[iClientIndex] = 2;

		if (g_radioSelect[iClientIndex] > 0)
		{
			if (FStrEq(pcmd, "menuselect"))
			{
				int iRadioCommand = atoi(arg1);
				if (iRadioCommand != 0)
				{
					if (g_radioSelect[iClientIndex] == 3)
						iRadioCommand += 10;
					else if (g_radioSelect[iClientIndex] == 2)
						iRadioCommand += 20;

					int team = g_threatTab[iClientIndex].team;
					Agent* agent;
					for (int i = 0; i < gpGlobals->maxClients; i++)
					{
						if (g_world->agents[i])
						{
							if (util::getTeam(g_world->agents[i]->getEdict()) == team
								&& VARS(pEntity) != g_world->agents[i]->pev)
							{
								agent = (Agent*) g_world->agents[i];
								agent->communication->pushRadioMessage(
									(ICommunication::RadioMessages::Enumeration) iRadioCommand, pEntity);
							}
						}
					}
				}
				g_radioSelect[iClientIndex] = 0;
			}
		}
	}

	if (util::g_isFakeClientCommand == 0)
	{
		if(FStrEq(pcmd, "say_team") || FStrEq(pcmd, "say"))
		{
			string msg(CMD_ARGS());
			msg = msg.substr(1, msg.length() - 2);

			if(!util::isAlive(pEntity)) // Dead chat
				handleSayDeadMessage(msg.c_str(), pEntity);
			else if (FStrEq(pcmd, "say")) // All chat
				handleSayMessage(msg.c_str(), pEntity);
			else if(FStrEq(pcmd, "say_team")) // Team chat
				handleSayTeamMessage(msg.c_str(), util::getTeam(pEntity), pEntity);
		}
		else if (pEntity == g_world->hostEdict && !IS_DEDICATED_SERVER())
		{
			if (iUserInMenu != MENU_NONE)
			{
				if (FStrEq(pcmd, "menuselect"))
				{
					if (!util::isNullString(arg1))
					{
						int iSelection = atoi(arg1);
						if (iUserInMenu == MENU_WAYPOINT_ADD)
						{
							switch (iSelection)
							{
							case 1:
							case 2:
							case 3:
							case 4:
							case 5:
							case 6:
							case 7:
								game::waypoints::add(iSelection - 1);
								iUserInMenu = MENU_NONE;
								break;
							case 8:
								game::waypoints::add(100);
								iUserInMenu = MENU_NONE;
								break;
							case 9:
								g_learnJumpWaypoint = true;
								iUserInMenu = MENU_NONE;
								break;
							case 10:
								iUserInMenu = MENU_NONE;
								break;
							}
						}
						else if (iUserInMenu == MENU_WAYPOINT_FLAGS)
						{
							switch (iSelection)
							{
							case 1:
								game::waypoints::changeFlags(WaypointFlags::noHostage, 1);
								break;

							case 2:
								game::waypoints::changeFlags(WaypointFlags::terrorist, 1);
								break;

							case 3:
								game::waypoints::changeFlags(WaypointFlags::counterTerrorist, 1);
								break;

							case 4:
								game::waypoints::changeFlags(WaypointFlags::ladder, 1);
								break;

							case 5:
								game::waypoints::changeFlags(WaypointFlags::noHostage, 0);
								break;

							case 6:
								game::waypoints::changeFlags(WaypointFlags::terrorist, 0);
								break;

							case 7:
								game::waypoints::changeFlags(WaypointFlags::counterTerrorist, 0);
								break;

							case 8:
								game::waypoints::changeFlags(WaypointFlags::ladder, 0);
								break;
							}
							iUserInMenu = MENU_NONE;
						}
						else if (iUserInMenu == MENU_WAYPOINT_MENU)
						{
							int waypoint2;
							switch (iSelection)
							{
							case 1:
								g_waypointOn &= 1;
								g_waypointOn ^= 1;
								iUserInMenu = MENU_NONE;
								break;

							case 2:
								g_waypointOn = true;
								g_iCreateStartIndex = game::waypoints::findNearest(pEntity->v.origin, 50.0);
								if (g_iCreateStartIndex == -1)
									EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0, ATTN_NORM, 0, 100);
								else
									EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_hudoff.wav", 1.0, ATTN_NORM, 0, 100);                           break;

							case 3:
								g_waypointOn = true;
								waypoint2 = game::waypoints::findNearest(pEntity->v.origin, 50.0);
								if (g_iCreateStartIndex != -1 && waypoint2 != -1)
									game::waypoints::createPath(g_iCreateStartIndex, waypoint2);
								else
									EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0, ATTN_NORM, 0, 100);                           break;

							case 4:
								g_waypointOn = true;
								g_iRemoveStartIndex = game::waypoints::findNearest(pEntity->v.origin, 50.0);
								if (g_iRemoveStartIndex == -1)
									EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0, ATTN_NORM, 0, 100);
								else
									EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_hudoff.wav", 1.0, ATTN_NORM, 0, 100);                           break;

							case 5:
								g_waypointOn = true;
								waypoint2 = game::waypoints::findNearest(pEntity->v.origin, 50.0);
								if (g_iRemoveStartIndex != -1 && waypoint2 != -1)
									game::waypoints::removePath(g_iRemoveStartIndex, waypoint2);
								else
									EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0, ATTN_NORM, 0, 100);
								break;

							case 6:
								g_waypointOn = true;
								util::showMenu(pEntity, menuWaypointAdd.ValidSlots, -1, false, util::language::translate(menuWaypointAdd.szMenuText));
								iUserInMenu = MENU_WAYPOINT_ADD;
								break;

							case 7:
								g_waypointOn = true;
								game::waypoints::deleteWaypoint();
								iUserInMenu = MENU_NONE;
								break;

							case 8:
								g_waypointOn = true;
								util::showMenu(pEntity, menuWaypointRadius.ValidSlots, -1, false, util::language::translate(menuWaypointRadius.szMenuText));
								iUserInMenu = MENU_WAYPOINT_SETRADIUS;
								break;

							case 9:
								util::showMenu(pEntity, menuWaypointMenu2.ValidSlots, -1, false, util::language::translate(menuWaypointMenu2.szMenuText));
								iUserInMenu = MENU_WAYPOINT_MENU2;
								break;

							case 10:
								iUserInMenu = MENU_NONE;
								break;
							}
						}
						else if (iUserInMenu == MENU_WAYPOINT_MENU2)
						{
							iUserInMenu = MENU_NONE;
							switch (iSelection)
							{
							case 1:
								{
									int iTPoints = 0;
									int iCTPoints = 0;
									int iGoalPoints = 0;
									int iRescuePoints = 0;
									int iCampPoints = 0;
									int iNoHostagePoints = 0;
									for (int i = 0; i < g_numWaypoints; i++)
									{
										if (g_waypoints[i]->flags & WaypointFlags::terrorist)
											iTPoints++;
										if (g_waypoints[i]->flags & WaypointFlags::counterTerrorist)
											iCTPoints++;
										if (g_waypoints[i]->flags & WaypointFlags::goal)
											iGoalPoints++;
										if (g_waypoints[i]->flags & WaypointFlags::rescue)
											iRescuePoints++;
										if (g_waypoints[i]->flags & WaypointFlags::camp)
											iCampPoints++;
										if (g_waypoints[i]->flags & WaypointFlags::noHostage)
											iNoHostagePoints++;
									}
									util::hostPrint(g_world->hostEdict, print_center,
										"Waypoints: %d - T Points: %d\n"
										"CT Points: %d - Goal Points: %d\n"
										"Rescue Points: %d - Camp Points: %d\n"
										"Block Hostage Points: %d\n",
										g_numWaypoints, iTPoints, iCTPoints, iGoalPoints,
										iRescuePoints, iCampPoints, iNoHostagePoints);
								}
								break;

							case 2:
								g_waypointOn = true;
								g_autoWaypointOn &= 1;
								g_autoWaypointOn ^= 1;
								util::hostPrint(g_world->hostEdict, print_center, g_autoWaypointOn ? "Auto-Waypoint is ON\n" : "Auto-Waypoint is OFF\n");
								break;

							case 3:
								g_waypointOn = true;
								util::showMenu(pEntity, menuWaypointFlag.ValidSlots, -1, false, util::language::translate(menuWaypointFlag.szMenuText));
								iUserInMenu = MENU_WAYPOINT_FLAGS;
								break;

							case 4:
								if (game::waypoints::areValid())
									game::waypoints::save();
								else
									util::hostPrint(g_world->hostEdict, print_center, "Waypoint not saved\n\nThere are errors, see console");
								break;

							case 5:
								game::waypoints::save();
								break;

							case 6:
								game::waypoints::load();
								break;

							case 7:
								if (game::waypoints::areValid())
									util::hostPrint(pEntity, print_center, "All Nodes work fine!");
								else
									util::hostPrint(pEntity, print_center, "There are errors, see console");
								break;

							case 8:
								g_editNoclip &= 1;
								g_editNoclip ^= 1;
								if (!g_editNoclip)
									pEntity->v.movetype = MOVETYPE_WALK;
								break;

							case 9:
								iUserInMenu = MENU_WAYPOINT_MENU;
								util::showMenu(g_world->hostEdict, menuWaypointMenu.ValidSlots, -1, false, util::language::translate(menuWaypointMenu.szMenuText));
								break;
							}
						}
						else if (iUserInMenu == MENU_WAYPOINT_SETRADIUS)
						{
							g_waypointOn = true;  // turn waypoints on in case

							const int iRadiusValue[] = {0, 8, 16, 32, 48, 64, 80, 96, 128};
							if (iSelection >= 1 && iSelection <= 9)
								game::waypoints::setRadius(iRadiusValue[iSelection - 1]);

							iUserInMenu = MENU_NONE;
						}
						else if (iUserInMenu == MENU_MAIN)
						{
							int index, count = 0;

							switch (iSelection)
							{
							case 1:
								game::gui::addAgent();
								iUserInMenu = MENU_NONE;
								break;

							case 2:
								util::showMenu(g_world->hostEdict, menuSelectTeam.ValidSlots, 
									-1, false, util::language::translate(menuSelectTeam.szMenuText));
								iUserInMenu = MENU_TEAMSELECT;
								break;

							case 3:
								game::gui::killAllAgents();
								iUserInMenu = MENU_NONE;
								break;

							case 4:
								game::gui::newroundAll();
								iUserInMenu = MENU_NONE;
								break;

							case 5:
								util::showMenu(pEntity, menuSelectTeam.ValidSlots, 
									-1, false, util::language::translate(menuSelectTeam.szMenuText));
								iUserInMenu = MENU_SERVERTEAMSELECT;
								break;

							case 6:
								game::gui::kickRandomAgent();

								for (index = 0; index < gpGlobals->maxClients; index++)
								{
									if (g_world->agents[index])
										count++;
								}

								count--;

								getServerCommandPrefix(command);
								strcat(command, "_quota");
								CVAR_SET_FLOAT(command, count);
								g_agentQuota = count;

								iUserInMenu = MENU_NONE;
								break;

							case 7:
								game::gui::removeAllAgents();
								iUserInMenu = MENU_NONE;
								break;

							case 10:
								iUserInMenu = MENU_NONE;
								break;
							}
						}
						else if (iUserInMenu == MENU_TEAMSELECT)
						{
							switch (iSelection)
							{
							case 1:
							case 2:
							case 5:
								storeAddAgentVars[0] = iSelection;
								util::showMenu(pEntity, menuSelectMind.ValidSlots, -1, false, util::language::translate(menuSelectMind.szMenuText));
								iUserInMenu = MENU_MINDSELECT;
								break;

							case 10:
								iUserInMenu = MENU_NONE;
								break;
							}
						}
						else if (iUserInMenu == MENU_MINDSELECT)
						{
							char team[4];
							sprintf(team, "%d", storeAddAgentVars[0]);

							char mind[64];
							switch (iSelection)
							{
							case 1:
								strcpy(mind, "reactive");
								break;
							case 2:
								strcpy(mind, "bdi");
								break;
							case 3:
								strcpy(mind, "bdiComm");
								break;
							case 4:
								strcpy(mind, "additionalTopic");
								break;
							case 5:
								strcpy(mind, "tournament");
								break;
							case 6:
								strcpy(mind, "navigationSample");
								break;
							case 7:
								strcpy(mind, "combatAndFinancesSample");
								break;
							case 8:
								strcpy(mind, "combatAndFinancesSample");
								break;
							case 9:
								strcpy(mind, "sensesSample");
								break;
							case 10:
								iUserInMenu = MENU_NONE;
								return;
							}

							game::gui::addAgent(team, NULL, mind, NULL);
							iUserInMenu = MENU_NONE;
						}
						else if (iUserInMenu == MENU_SERVERTEAMSELECT)
						{
							switch (iSelection)
							{
							case 1:
							case 2:
								// Turn off CVARS if specified team 
								CVAR_SET_STRING("mp_limitteams", "0");
								CVAR_SET_STRING("mp_autoteambalance", "0");

							case 5:
								iFillServerTeam = iSelection;
								for (int index = 0; index < gpGlobals->maxClients; index++)
								{
									if (!g_agentCreationTabs[index].bNeedsCreation)
									{
										g_agentCreationTabs[index].bNeedsCreation = true;
										g_agentCreationTabs[index].pCallingEnt = pEntity;
										g_agentCreationTabs[index].name[0] = '\0';
										sprintf(g_agentCreationTabs[index].team, "%d", iFillServerTeam);
										strcpy(g_agentCreationTabs[index].mind, "default");
									}
								}
								if (g_agentCreationTime == 0.0)
									g_agentCreationTime = g_world->getTime();
								break;

							case 10:
								iUserInMenu = MENU_NONE;
								break;
							}
						}
					}
					if (g_bIsMMPlugin)
						RETURN_META (MRES_SUPERCEDE);
					return;
				}
			}
		}
	}

	if (g_bIsMMPlugin)
		RETURN_META(MRES_IGNORED);
	(*other_gFunctionTable.pfnClientCommand)(pEntity);
}


/**
* \brief Handles server commands.
**/
void handleServerCommand(void)
{
	const char *pcmd = CMD_ARGV(1);
	const char *arg1 = CMD_ARGV(2);
	const char *arg2 = CMD_ARGV(3);
	const char *arg3 = CMD_ARGV(4);
	const char *arg4 = CMD_ARGV(5);
	const char *arg5 = CMD_ARGV(6);

	char unknownCommandMessage[128];
	char command[16];

	util::getModName(command);
	strcpy(unknownCommandMessage, command);
	strcat(unknownCommandMessage, " Agent: Unknown command.\nType '");
	getServerCommandPrefix(command);
	strcat(unknownCommandMessage, command);
	strcat(unknownCommandMessage, " ?' for a list of available commands.\n");

	if (FStrEq(pcmd, "addagent") || FStrEq(pcmd, "add"))
		game::gui::addAgent(arg1, arg2, arg3, arg4);

	else if (FStrEq(pcmd, "fillserver") || FStrEq(pcmd, "fill"))
	{
		if (util::isNullString(arg2))
			game::gui::fillServer(atoi(arg1), -1);
		else
			game::gui::fillServer(atoi(arg1), atoi(arg2));
	}

	else if (FStrEq(pcmd, "removeagents") || FStrEq(pcmd, "remove"))
		game::gui::removeAllAgents();

	else if (FStrEq(pcmd, "killbots") || FStrEq(pcmd, "kill"))
		game::gui::killAllAgents();

	else if (FStrEq(pcmd, "removerandomagent") || FStrEq(pcmd, "kick"))
	{
		game::gui::kickRandomAgent();

		int index, count = 0;
		for (index = 0; index < gpGlobals->maxClients; index++)
		{
			if (g_world->agents[index])
				count++;
		}

		count--;

		char command[32];
		getServerCommandPrefix(command);
		strcat(command, "_quota");
		CVAR_SET_FLOAT(command, count);
		g_agentQuota = count;
	}

	else if (FStrEq(pcmd, "newround"))
		game::gui::newroundAll();

	else if (FStrEq(pcmd, "wpnmode"))
	{
		int iSelection = atoi(arg1);
		if (iSelection >= 1 && iSelection <= 7);
			//userSelectWeaponMode(iSelection);
		else
			SERVER_PRINT(util::language::translate("Valid weapon mode is from 1 to 7\n"));
	}
	else if (FStrEq(pcmd, "about"))
	{
		SERVER_PRINT(util::varArgs("=========== VERSION INFORMATION ===========\n"
			"Technical University of Lisbon, Portugal\n"
			"Autonomous Agents and Multiagent Systems\n\n"
			"AASM Counter-Strike Agents \n"
			"v1.0 (build %u, " __DATE__ ") 2006/2007\n"
			"===========================================\n", getBuildNumber()));

		if (g_numWaypoints > 0)
			SERVER_PRINT(util::varArgs((char *) util::language::translate("Waypoint made by: %s\n"), g_waypointer));
		else
			SERVER_PRINT(util::language::translate("WARNING: No waypoint file is found. You can't add agents.\n"));
	}

	else if (FStrEq(pcmd, "?") || FStrEq(pcmd, "help"))
	{
		char buffer[512];
		char prefix[16];
		getServerCommandPrefix(prefix);

		strcpy(buffer, "==== Available Server Commands ====\n");
		strcat(buffer, prefix);
		strcat(buffer, " about - Display the version information.\n");
		strcat(buffer, prefix);
		strcat(buffer, " add - Add an agent.\n");
		strcat(buffer, prefix);
		strcat(buffer, " fill - Fill server with agents.\n");
		strcat(buffer, prefix);
		strcat(buffer, " removeagents - Remove all agents.\n");
		strcat(buffer, prefix);
		strcat(buffer, " killbots - Kill all agents.\n");
		strcat(buffer, prefix);
		strcat(buffer, " removerandomagent - Remove a random agent.\n");
		strcat(buffer, prefix);
		strcat(buffer, " newround - Kill all players.\n");
		strcat(buffer, prefix);
		strcat(buffer, " wpnmode - Select weapon mode.\n");
		strcat(buffer, prefix);
		strcat(buffer, " botsvotemap - Let all dead agents vote for a map.\n");

		SERVER_PRINT(buffer);
	}

	else if (!IS_DEDICATED_SERVER() && !FNullEnt(g_world->hostEdict))
	{
		if (FStrEq(pcmd, "waypoint") || FStrEq(pcmd, "wp"))
		{
			if (FStrEq(arg1, "init"))
			{
				game::gui::removeAllAgents();
				game::waypoints::reset();
				g_waypointOn = true;
				util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Waypoint cleared\n"));
			}
			else if (FStrEq(arg1, "on"))
			{
				g_waypointOn = true;
				util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Waypoint Editing is ON\n"));

				if (FStrEq(arg2, "noclip"))
				{
					g_editNoclip &= 1;
					g_editNoclip ^= 1;
					if (!g_editNoclip)
					{
						util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Noclip Cheat is OFF\n"));
						g_world->hostEdict->v.movetype = MOVETYPE_WALK;
					}
					else
						util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Noclip Cheat is ON\n"));
				}
			}
			else if (FStrEq(arg1, "off"))
			{
				g_waypointOn = false;
				g_editNoclip = false;
				g_world->hostEdict->v.movetype = MOVETYPE_WALK;
				util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Waypoint editing turned OFF\n"));
			}
			else if (FStrEq(arg1, "find"))
			{
				g_findWPIndex = atoi(arg2);

				if (g_findWPIndex < g_numWaypoints)
					util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Showing direction to waypoint\n"));
				else
					g_findWPIndex = -1;
			}
			else if (FStrEq(arg1, "add"))
			{
				g_waypointOn = true;  // turn waypoints on
				iUserInMenu = MENU_WAYPOINT_ADD;
				util::showMenu(g_world->hostEdict, menuWaypointAdd.ValidSlots, 
					-1, false, util::language::translate(menuWaypointAdd.szMenuText));
			}
			else if (FStrEq(arg1, "delete"))
			{
				g_waypointOn = true;  // turn waypoints on
				game::waypoints::deleteWaypoint();
			}
			else if (FStrEq(arg1, "save"))
			{
				const char *szWaypointSaveMessage = "Waypoints saved\n";

				if (FStrEq(arg2, "nocheck"))
				{
					game::waypoints::save();
					util::hostPrint(g_world->hostEdict, print_console, util::language::translate(szWaypointSaveMessage));
				}
				else if (FStrEq(arg2, "oldformat"))
				{
					if (FStrEq(arg3, "nocheck"))
					{
						game::waypoints::saveOldFormat();
						util::hostPrint(g_world->hostEdict, print_console, util::language::translate(szWaypointSaveMessage));
					}
					else if (game::waypoints::areValid())
					{
						game::waypoints::saveOldFormat();
						util::hostPrint(g_world->hostEdict, print_console, util::language::translate(szWaypointSaveMessage));
					}
				}
				else if (game::waypoints::areValid())
				{
					game::waypoints::save();
					util::hostPrint(g_world->hostEdict, print_console, util::language::translate(szWaypointSaveMessage));
				}
			}
			else if (FStrEq(arg1, "load"))
			{
				if (game::waypoints::load())
					util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Waypoints loaded\n"));
			}
			else if (FStrEq(arg1, "check"))
			{
				if (game::waypoints::areValid())
					util::hostPrint(g_world->hostEdict, print_console, util::language::translate("All nodes work fine!\n"));
			}
			else if (FStrEq(arg1, "flags"))
			{
				iUserInMenu = MENU_WAYPOINT_FLAGS;
				util::showMenu(g_world->hostEdict, menuWaypointFlag.ValidSlots, 
					-1, false, util::language::translate(menuWaypointFlag.szMenuText));
			}
			else if (FStrEq(arg1, "setradius"))
			{
				game::waypoints::setRadius(atoi(arg2));
			}
			else if (FStrEq(arg1, "cache"))
			{
				g_cacheWaypointIndex = game::waypoints::findNearest(g_world->hostEdict->v.origin);
				util::hostPrint(g_world->hostEdict, print_console, 
					util::language::translate("Waypoint Nr. %d now cached\n"), g_cacheWaypointIndex);
			}
			else if (FStrEq(arg1, "teleport"))
			{
				int iTelIndex = atoi(arg2);
				if (iTelIndex < g_numWaypoints)
				{
					g_engfuncs.pfnSetOrigin(g_world->hostEdict, g_waypoints[iTelIndex]->origin);
					g_waypointOn = true;
					g_editNoclip = true;
				}
			}
			else if (FStrEq(arg1, "menu"))
			{
				iUserInMenu = MENU_WAYPOINT_MENU;
				util::showMenu(g_world->hostEdict, menuWaypointMenu.ValidSlots, -1, 
					false, util::language::translate(menuWaypointMenu.szMenuText));
			}
			else
			{
				if (g_waypointOn)
					util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Waypoints are ON\n"));
				else
					util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Waypoints are OFF\n"));
			}
		}
		else if (FStrEq(pcmd, "pathwaypoint") || FStrEq(pcmd, "pwp"))
		{
			if (FStrEq(arg1, "add"))
			{
				if (arg2[0] != '\0')
					game::waypoints::createPath(atoi(arg2));
				else
					game::waypoints::createPath(g_cacheWaypointIndex);
			}
			else if (FStrEq(arg1, "connect"))
			{
				int w = game::waypoints::findNearest(g_world->hostEdict->v.origin, 50.0);
				if (arg2[0] != '\0' && w != -1)
				{
					game::waypoints::createPath(w, atoi(arg2));
					game::waypoints::createPath(atoi(arg2), w);
				}
			}
			else if (FStrEq(arg1, "delete"))
			{
				if (arg2[0] != '\0')
					game::waypoints::removePath(atoi(arg2));
				else
					game::waypoints::removePath(g_cacheWaypointIndex);
			}
			else if (FStrEq(arg1, "disconnect"))
			{
				int w = game::waypoints::findNearest(g_world->hostEdict->v.origin, 50.0);
				if (arg2[0] != '\0' && w != -1)
				{
					game::waypoints::removePath(w, atoi(arg2));
					game::waypoints::removePath(atoi(arg2), w);
				}
			}
			else if (FStrEq(arg1, "create1"))
			{
				g_iCreateStartIndex = game::waypoints::findNearest(g_world->hostEdict->v.origin, 50.0);
				if (g_iCreateStartIndex == -1)
					EMIT_SOUND_DYN2(g_world->hostEdict, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0, ATTN_NORM, 0, 100);
				else
					EMIT_SOUND_DYN2(g_world->hostEdict, CHAN_WEAPON, "common/wpn_hudoff.wav", 1.0, ATTN_NORM, 0, 100);
			}
			else if (FStrEq(arg1, "remove1"))
			{
				g_iRemoveStartIndex = game::waypoints::findNearest(g_world->hostEdict->v.origin, 50.0);
				if (g_iRemoveStartIndex == -1)
					EMIT_SOUND_DYN2(g_world->hostEdict, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0, ATTN_NORM, 0, 100);
				else
					EMIT_SOUND_DYN2(g_world->hostEdict, CHAN_WEAPON, "common/wpn_hudoff.wav", 1.0, ATTN_NORM, 0, 100);
			}
			else if (FStrEq(arg1, "create2"))
			{
				int waypoint2 = game::waypoints::findNearest(g_world->hostEdict->v.origin, 50.0);
				if (g_iCreateStartIndex != -1 && waypoint2 != -1)
					game::waypoints::createPath(g_iCreateStartIndex, waypoint2);
				else
					EMIT_SOUND_DYN2(g_world->hostEdict, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0, ATTN_NORM, 0, 100);
			}
			else if (FStrEq(arg1, "remove2"))
			{
				int waypoint2 = game::waypoints::findNearest(g_world->hostEdict->v.origin, 50.0);
				if (g_iRemoveStartIndex != -1 && waypoint2 != -1)
					game::waypoints::removePath(g_iRemoveStartIndex, waypoint2);
				else
					EMIT_SOUND_DYN2(g_world->hostEdict, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0, ATTN_NORM, 0, 100);
			}
		}
		else if (FStrEq(pcmd, "autowaypoint") || FStrEq(pcmd, "awp"))
		{
			if (FStrEq(arg1, "on"))
			{
				g_autoWaypointOn = true;
				g_waypointOn = true;  // turn this on just in case
			}
			else if (FStrEq(arg1, "off"))
				g_autoWaypointOn = false;

			if (g_autoWaypointOn)
				util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Auto-Waypoint is ON\n"));
			else
				util::hostPrint(g_world->hostEdict, print_console, util::language::translate("Auto-Waypoint is OFF\n"));
		}
		else if (FStrEq(pcmd, "sayTeam") || FStrEq(pcmd, "sayT"))
		{
			if (FStrEq(arg1, "T"))
				handleSayTeamMessage(arg2, CounterStrikeTeams::terrorist, NULL);
			else if (FStrEq(arg1, "CT"))
				handleSayTeamMessage(arg2, CounterStrikeTeams::counterTerrorist, NULL);
		}
		else if (FStrEq(pcmd, "say"))
			handleSayMessage(arg1, NULL);
		else if (FStrEq(pcmd, "sayDead") || FStrEq(pcmd, "sayD"))
			handleSayDeadMessage(arg1, NULL);
		else if (FStrEq(pcmd, "aasmagentmenu") || FStrEq(pcmd, "menu"))
		{
			iUserInMenu = MENU_MAIN;
			util::showMenu(g_world->hostEdict, menuMain.ValidSlots, -1, false, util::language::translate(menuMain.szMenuText));
		}
		else
			SERVER_PRINT(unknownCommandMessage);
	}

	else
		SERVER_PRINT(unknownCommandMessage);
}
/**
*
**/
void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
{
	g_world->setIsMapInitialized(true);

	char command[128];
	strcpy(command, "exec addons/aasmCsAgents");
	strcat(command, AASM_GROUP_ID);
	strcat(command, "/config/aasmCsAgents.cfg\n");
	SERVER_COMMAND(command);

	// set the agent check time
	g_flBotCheckTime = g_world->getTime() + 2.0;

	if (g_bIsMMPlugin)
		RETURN_META(MRES_IGNORED);
	(*other_gFunctionTable.pfnServerActivate)(pEdictList, edictCount, clientMax);

	game::waypoints::calculateVisibility();
}

/**
*
**/
void ServerDeactivate( void )
{
	util::debug::log("server shuting down...");
	util::debug::conclude();

	g_world->setIsMapInitialized(false);

	if (g_bIsMMPlugin)
		RETURN_META(MRES_IGNORED);
	(*other_gFunctionTable.pfnServerDeactivate)();
}


/**
*
**/
void ServerActivate_Post( edict_t *pEdictList, int edictCount, int clientMax )
{
	game::waypoints::calculateVisibility();
	RETURN_META(MRES_IGNORED);
}

void handleSayDeadMessage(const char* msg, edict_t* sender)
{
	Agent* agent;
	for (int i = 0; i < gpGlobals->maxClients; i++)
	{
		if (g_world->agents[i])
		{
			if ((sender == NULL) || (VARS(sender) != g_world->agents[i]->pev))
			{
				agent = (Agent*) g_world->agents[i];
				agent->communication->pushSayDeadMessage(msg, sender);
			}
		}
	}
}
void handleSayMessage(const char* msg, edict_t* sender)
{
	Agent* agent;
	for (int i = 0; i < gpGlobals->maxClients; i++)
	{
		if (g_world->agents[i])
		{
			if ((sender == NULL) || (VARS(sender) != g_world->agents[i]->pev))
			{
				agent = (Agent*) g_world->agents[i];
				agent->communication->pushSayMessage(msg, sender);
			}
		}
	}
}
void handleSayTeamMessage(const char* msg, CounterStrikeTeams::Enumeration team, edict_t* sender)
{
	Agent* agent;
	for (int i = 0; i < gpGlobals->maxClients; i++)
	{
		if (g_world->agents[i])
		{
			if (((sender == NULL) || (VARS(sender) != g_world->agents[i]->pev)) && (team == g_threatTab[i].team))
			{
				agent = (Agent*) g_world->agents[i];
				agent->communication->pushSayTeamMessage(msg, sender);
			}
		}
	}
}
/**
* \brief Gets the server command prefix.
**/
void getServerCommandPrefix(char* prefix)
{
	strcpy(prefix, "aasm");
	strcat(prefix, AASM_GROUP_ID);
}